﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Space_Cleaner.SpaceCleaner
{
    static class Collision
    {
        private static float COR = 0.5f; //Coefficient of restitution (between 0 and 1)

        public static bool collide(SC_SpaceObject _Object1, SC_SpaceObject _Object2)
        {
            Vector2 _VelocityT1;
            Vector2 _VelocityZ1;
            Vector2 _VelocityT2;
            Vector2 _VelocityZ2;

            //getting vector for the positionsdifference
            Vector2 Direction = _Object1.Position - _Object2.Position;
            Direction.Normalize();

            splitVelocity(_Object1.Velocity, Direction, out _VelocityT1, out _VelocityZ1);
            splitVelocity(_Object2.Velocity, Direction, out _VelocityT2, out _VelocityZ2);

            float AbsValueZ1 = 0;
            float AbsValueZ2 = 0;
            float OldAbsValueZ1 = 0;
            float OldAbsValueZ2 = 0;

            //check if the velocity has the same direction
            if (Math.Sign(_VelocityZ1.X) == Math.Sign(_VelocityZ2.X)
                && Math.Sign(_VelocityZ1.Y) == Math.Sign(_VelocityZ2.Y))
            {
                OldAbsValueZ1 = _VelocityZ1.Length();
                OldAbsValueZ2 = _VelocityZ2.Length();
            }
            else
            {
                OldAbsValueZ1 = _VelocityZ1.Length();
                OldAbsValueZ2 = -_VelocityZ2.Length();
            }

            getNewVelocity(_Object1.Mass, _Object2.Mass, OldAbsValueZ1, OldAbsValueZ2, out AbsValueZ1, out AbsValueZ2);

            _VelocityZ1.Normalize();
            _VelocityZ2.Normalize();

            _VelocityZ1 *= Math.Abs(AbsValueZ1);
            _VelocityZ2 *= Math.Abs(AbsValueZ2);
            
            _Object1.Velocity = _VelocityT1 + _VelocityZ1;
            _Object2.Velocity = _VelocityT2 - _VelocityZ2;

            //TODO: Objekte müssen auseinander gesetzt werden damit keine weitere Kollision erkannt wird

            _Object1.Position += Direction;
            _Object2.Position -= Direction;

            return true;
        }

        public static Boolean intersect(SC_SpaceObject _Object1, SC_SpaceObject _Object2)
        {
            float Distance = (_Object1.Position - _Object2.Position).Length();

            if (Distance <= _Object1.CollisionRadius + _Object2.CollisionRadius)
                return true;
            else
                return false;
        }

        private static void getNewVelocity(float _Mass1, float _Mass2, float _Velocity1, float _Velocity2, out float _NewVelocity1, out float _NewVelocity2)
        {
            float MassSum = _Mass1 + _Mass2;
            float Temp = (_Mass1 * _Velocity1 + _Mass2 * _Velocity2) / MassSum;
            _NewVelocity1 = Temp - (_Mass2 * (_Velocity1 - _Velocity2) * COR) / MassSum;
            _NewVelocity2 = Temp - (_Mass1 * (_Velocity2 - _Velocity1) * COR) / MassSum;
        }

        //divide the Velocity Vector in central(z) and tangential(t) Components
        private static void splitVelocity(Vector2 _Velocity, Vector2 _Direction, out Vector2 _VelocityT, out Vector2 _VelocityZ)
        {
            float Dot = Vector2.Dot(_Velocity, _Direction);//_Direction muss normiert sein, _Velocity darf nicht
            _VelocityZ = _Direction * Dot;
            _VelocityT = _Velocity - _VelocityZ;
        }

        public static SC_SpaceObject[] divideObject(SC_SpaceObject _Object)
        {
            SC_SpaceObject[] Objects = new SC_SpaceObject[4];
            Vector2 Dircetion = _Object.Velocity;
            Dircetion.Normalize();

            Dircetion = Vector2.Transform(Dircetion, Matrix.CreateRotationZ(90.0f));
            Dircetion *= (_Object.CollisionRadius / 1.9f);

            Objects[0] = SC_SpaceObjectList.GetNewTrash();
            Objects[1] = SC_SpaceObjectList.GetNewTrash();

            Objects[0].CopyValues(_Object, 0.5f);
            Objects[1].CopyValues(_Object, 0.5f);

            Objects[0].Position += Dircetion;
            Objects[1].Position -= Dircetion;

            Dircetion.Normalize();
            Objects[0].Velocity += Dircetion/5; 

            return Objects;
        }

        public static void doCollisionDetection()
        {
            LinkedList<SC_SpaceObject> physicsObjects = SC_World.getInstance().getPhysicsObjects();
            bool IsCollision = false;

            SC_World world = SC_World.getInstance();
            SC_Earth earth = world.getEarth();

            //Collisiondetection with earth
            foreach (SC_SpaceObject obj in physicsObjects)
            {
                if (obj != earth && earth != null && Collision.intersect(earth, obj) && obj.Activated)
                {
                    obj.Activated = false;
                    world.RemoveObject(obj);
                    world.addEffect(SC_TextureManager.getTexture("hit"), obj, 15);
                    break;
                }
            }

            //Collisiondetection
            foreach (SC_SpaceObject obj1 in physicsObjects)
            {
                foreach (SC_SpaceObject obj2 in physicsObjects)
                {
                    if (obj1 != obj2 && Collision.intersect(obj1, obj2) && obj1.Activated && obj2.Activated)
                    {
                        if (obj1 is SC_SpaceCraft) ((SC_SpaceCraft)obj1).Shield--;
                        if (obj2 is SC_SpaceCraft) ((SC_SpaceCraft)obj2).Shield--;
                        IsCollision = true;
                        splitByCollision(obj1, obj2);
                        world.addEffect(SC_TextureManager.getTexture("hit"), obj1, 15);

                        break;
                    }
                }
                if (IsCollision == true)
                {
                  //  hud.CollisionCount += 1;
                    break;
                }
            }
        }

        public static bool doSimpleCollisionDetection(SC_SpaceObject _Object) //only test if there is a collision, no splitting
        {
            LinkedList<SC_SpaceObject> physicsObjects = SC_World.getInstance().getPhysicsObjects();
            bool IsCollision = false;

            //Collisiondetection
            foreach (SC_SpaceObject obj1 in physicsObjects)
            {
                if (obj1 != _Object && Collision.intersect(obj1, _Object) && obj1.Activated && _Object.Activated)
                {
                    IsCollision = true;
                    break;
                }
                
            }
            return IsCollision;
        }

        private static void splitByCollision(SC_SpaceObject Obj1, SC_SpaceObject Obj2)
        {
            SC_World world = SC_World.getInstance();
            LinkedList<SC_SpaceObject> physicsObjects = world.getPhysicsObjects();

            Collision.collide(Obj1, Obj2);
            
            //Abruchbedingungen bei zu vielen Collisionen
            //if (physicsObjects.Count < 260) return;
            //if (Obj1.Size < 0.05f && Obj2.Size < 0.05f) return;

            //Splitten der Objecte
            SC_SpaceObject[] Objects;
            if (Obj1.Size > 0.05f && (!(Obj1 is SC_SpaceCraft) || ((SC_SpaceCraft)Obj1).Shield <= 0))
            {
                Objects = Collision.divideObject(Obj1);
                world.addObject(Objects[0]);
                world.addObject(Objects[1]);
            }

            if (Obj2.Size > 0.05f && (!(Obj1 is SC_SpaceCraft) || ((SC_SpaceCraft)Obj1).Shield <= 0))
            {
                Objects = Collision.divideObject(Obj2);
                world.addObject(Objects[0]);
                world.addObject(Objects[1]);
            }

            //Entfernen der original trashs
            if ((!(Obj1 is SC_SpaceCraft) || ((SC_SpaceCraft)Obj1).Shield <= 0))
            {
                Obj1.Activated = false;
                world.RemoveObject(Obj1);
                if (Obj1 is SC_Trash) SC_SpaceObjectList.GiveTrashBack((SC_Trash)Obj1);
            }

            if ((!(Obj2 is SC_SpaceCraft) || ((SC_SpaceCraft)Obj2).Shield <= 0))
            {
                Obj2.Activated = false;
                world.RemoveObject(Obj2);
                if (Obj2 is SC_Trash) SC_SpaceObjectList.GiveTrashBack((SC_Trash)Obj2);
            }
        }
    }
}
